/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2024.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* t_nanosleep.c

   Demonstrate the use of nanosleep() to sleep for an interval
   specified in nanoseconds.

   See also t_clock_nanosleep.c.
*/
#define _POSIX_C_SOURCE 199309
#define _XOPEN_SOURCE 600
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static void
sigintHandler(int sig)
{
    return;                     /* Just interrupt nanosleep() */
}

int
main(int argc, char *argv[])
{
    struct timeval start, finish;
    struct timespec request;
    struct sigaction sa;
    int s;

    if (argc != 3 || strcmp(argv[1], "--help") == 0){
        printf("%s secs nanosecs\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Allow SIGINT handler to interrupt nanosleep() */

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sigintHandler;
    if (sigaction(SIGINT, &sa, NULL) == -1){
        puts("sigaction");
        exit(EXIT_FAILURE);
    }

    if(clock_gettime(CLOCK_REALTIME, &request) == -1){
        printf("Error clock_gettime %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);        
    }
    request.tv_sec += atoi(argv[1]);
    request.tv_nsec += atoi(argv[2]); 
    if (gettimeofday(&start, NULL) == -1){
        puts("gettimeofday");
        exit(EXIT_FAILURE);
    }    
    for (;;) {
        s = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &request, NULL);
        if (s != 0){
            if(s == EINTR){
                puts("Interrupted by signal");   
            }
            else{
                puts("Clock nano_sleep");
                exit(EXIT_FAILURE);
            }
        }

        if (gettimeofday(&finish, NULL) == -1){
            puts("gettimeofday");
            exit(EXIT_FAILURE);
        }
        printf("Slept for: %9.6f secs\n", finish.tv_sec - start.tv_sec +
                        (finish.tv_usec - start.tv_usec) / 1000000.0);

        if (s == 0)
            break;                      /* nanosleep() completed */

        printf("Remaining: %2ld.%09ld\n", (long) request.tv_sec, request.tv_nsec);
    }

    printf("Sleep complete\n");
    exit(EXIT_SUCCESS);
}